import os

import pandas as pd


from src.config.constants import (
    CLASSIFICATION_CATEGORIES,
    EXPORT_SETTINGS,
    MATCH_STATUS,
)
from src.config.exceptions import DataConsistencyError, ExportError
from src.utils.excel_utils import ExcelUtils


class ResultProcessor:
    """负责处理和优化匹配结果"""

    def __init__(self):
        """初始化结果处理器"""
        self.excel_utils = ExcelUtils()

    def detect_file_issues(self, file_path):
        """检测Excel文件的潜在问题

        Args:
            file_path: 要检查的文件路径

        Returns:
            list: 发现的问题列表，如果没有问题则返回空列表
        """
        issues = []

        # 检查文件是否存在（这个检查在导入阶段也会做）
        if not os.path.exists(file_path):
            issues.append(f"文件不存在")
            return issues

        # 检查文件大小
        file_size = os.path.getsize(file_path)
        if file_size == 0:
            issues.append(f"文件为空")
        elif file_size < 100:  # 小于100字节的文件可能有问题
            issues.append(f"文件太小，可能不完整")

        # 尝试打开文件验证格式和完整性
        try:
            # 使用pandas尝试读取文件
            with pd.ExcelFile(file_path) as excel_file:
                # 检查是否有工作表
                if len(excel_file.sheet_names) == 0:
                    issues.append(f"文件不包含任何工作表")

                # 尝试读取第一个工作表
                try:
                    first_sheet = excel_file.sheet_names[0]
                    data = pd.read_excel(
                        excel_file, sheet_name=first_sheet, nrows=5
                    )

                    # 检查数据是否为空
                    if data.empty:
                        issues.append(f"第一个工作表没有数据")
                except Exception:
                    issues.append(f"无法读取第一个工作表的数据")
        except Exception as e:
            issues.append(f"文件格式无效或已损坏: {str(e)}")

        return issues

    def process_results(self, matched_data):
        """处理匹配结果

        Args:
            matched_data: 匹配后的数据

        Returns:
            处理后的结果数据
        """
        if matched_data is None or matched_data.empty:
            return pd.DataFrame()

        # 创建结果副本
        result = matched_data.copy()

        # 确保必要的列存在
        required_columns = ["客户简称", "规格型号", "数量", "备注", "包装类型"]
        for col in required_columns:
            if col not in result.columns:
                result[col] = pd.NA

        # 数据合并：将分类数据合并到统一的数据结构中
        processed_result = self._merge_classified_data(result)

        # 特殊处理：处理特殊情况和边界条件
        processed_result = self._apply_special_processing(processed_result)

        # 数据排序：按客户简称、预完工日期、规格型号等排序
        processed_result = self._sort_data(processed_result)

        return processed_result

    def _merge_classified_data(self, data):
        """合并分类数据

        Args:
            data: 待合并的数据

        Returns:
            合并后的数据
        """
        # 按客户、日期和产品类别分组，合并相同类型的记录
        # 这里是简化实现，实际应用中可能需要更复杂的合并逻辑

        # 创建产品类别映射，用于后续数据合并
        category_map = {
            CLASSIFICATION_CATEGORIES["REUSE"]: "复用项",
            CLASSIFICATION_CATEGORIES[
                "REFERENCE_INDEPENDENT"
            ]: "参考项-仅独立包装",
            CLASSIFICATION_CATEGORIES[
                "REFERENCE_COMBINATION"
            ]: "参考项-仅组合包装",
            CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"]: "参考项-混合包装",
            MATCH_STATUS["DESIGN"]: "设计项",
        }

        # 添加产品类别描述列
        data["产品类别描述"] = data["备注"].map(category_map).fillna("未知")

        # 按客户和日期分组，便于后续处理
        grouped = data.groupby(["客户简称", "预完工日期"], observed=True)

        # 为每个客户的每个日期创建一个处理单元
        processed_groups = []
        for group_key, group_data in grouped:
            # 复制组数据
            processed_group = group_data.copy()

            # 在组内标记组合包装的关系
            processed_group = self._mark_combination_relationships(
                processed_group
            )

            processed_groups.append(processed_group)

        # 合并所有处理过的组
        merged_data = pd.concat(processed_groups)

        return merged_data

    def _mark_combination_relationships(self, group_data):
        """标记组合包装的关系

        Args:
            group_data: 客户日期组的数据

        Returns:
            标记后的组数据
        """
        # 复制数据以避免修改原数据
        processed_data = group_data.copy()

        # 检查是否存在组合包装
        has_combination = "组合包装" in processed_data["包装类型"].values

        if not has_combination:
            return processed_data

        # 为组合包装添加标记
        combination_mask = processed_data["包装类型"] == "组合包装"

        # 创建组合ID列，用于标识同一组合的包装
        processed_data["组合ID"] = pd.NA

        # 为每个组合包装分配一个唯一的ID
        combination_id = 1
        for idx, row in processed_data.iterrows():
            if row["包装类型"] == "组合包装":
                # 查找与当前组合包装相关的所有行
                # 实际实现应基于合并单元格信息或其他标识组合关系的字段
                # 这里简化处理，将同一规格型号的组合包装标记为同一组
                spec_mask = processed_data["规格型号"] == row["规格型号"]
                pack_mask = processed_data["包装类型"] == "组合包装"
                combined_mask = spec_mask & pack_mask

                # 为匹配的行分配组合ID
                processed_data.loc[combined_mask, "组合ID"] = combination_id
                combination_id += 1

                # 避免重复处理
                processed_data.loc[combined_mask, "包装类型"] = (
                    "已标记组合包装"
                )

        # 恢复包装类型名称
        processed_data["包装类型"] = processed_data["包装类型"].replace(
            "已标记组合包装", "组合包装"
        )

        return processed_data

    def _apply_special_processing(self, data):
        """应用特殊处理逻辑

        Args:
            data: 待处理的数据

        Returns:
            处理后的数据
        """
        # 复制数据以避免修改原数据
        processed_data = data.copy()

        # 处理数量为0的情况
        zero_qty_mask = processed_data["数量"] == 0
        if zero_qty_mask.any():
            processed_data.loc[zero_qty_mask, "备注"] = "数量为0，无需包装"
            processed_data.loc[zero_qty_mask, "包装类型"] = "无需包装"

        # 处理数量为负数的异常情况
        neg_qty_mask = processed_data["数量"] < 0
        if neg_qty_mask.any():
            processed_data.loc[neg_qty_mask, "备注"] = (
                "数量异常（负数），需审核"
            )
            processed_data.loc[neg_qty_mask, "包装类型"] = "异常数量"

        # 处理规格型号为空的情况
        empty_spec_mask = processed_data["规格型号"].isna() | (
            processed_data["规格型号"] == ""
        )
        if empty_spec_mask.any():
            processed_data.loc[empty_spec_mask, "备注"] = (
                "规格型号为空，需审核"
            )
            processed_data.loc[empty_spec_mask, "包装类型"] = "信息不完整"

        # 处理客户简称为空的情况
        empty_customer_mask = processed_data["客户简称"].isna() | (
            processed_data["客户简称"] == ""
        )
        if empty_customer_mask.any():
            processed_data.loc[empty_customer_mask, "备注"] = (
                "客户信息为空，需审核"
            )

        # 处理预完工日期为空的情况
        if "预完工日期" in processed_data.columns:
            empty_date_mask = processed_data["预完工日期"].isna() | (
                processed_data["预完工日期"] == ""
            )
            if empty_date_mask.any():
                processed_data.loc[empty_date_mask, "备注"] = (
                    "预完工日期为空，使用默认分组"
                )

        # 处理特殊标记的组合包装
        if "组合ID" in processed_data.columns:
            combination_with_id_mask = processed_data["组合ID"].notna()
            if combination_with_id_mask.any():
                # 为组合包装添加特殊标记
                processed_data.loc[combination_with_id_mask, "备注"] = (
                    processed_data.loc[
                        combination_with_id_mask, "备注"
                    ].fillna("")
                    + "（组合包装，组ID："
                    + processed_data.loc[
                        combination_with_id_mask, "组合ID"
                    ].astype(str)
                    + "）"
                )

        return processed_data

    def _sort_data(self, data):
        """对数据进行排序

        Args:
            data: 待排序的数据

        Returns:
            排序后的数据
        """
        # 如果有预完工日期列，按预完工日期排序
        if "预完工日期" in data.columns:
            data = data.sort_values(by=["预完工日期", "客户简称", "规格型号"])
        else:
            data = data.sort_values(by=["客户简称", "规格型号"])

        # 重置索引
        data = data.reset_index(drop=True)

        return data

    def optimize_results(self, processed_data):
        """优化处理后的结果

        Args:
            processed_data: 处理后的数据

        Returns:
            优化后的结果
        """
        import logging
        logger = logging.getLogger("result_processing")
        
        if processed_data is None or processed_data.empty:
            logger.warning("没有数据需要优化")
            return processed_data
        
        # 创建数据副本以避免修改原始数据
        optimized_data = processed_data.copy()
        
        # 1. 优化组合包装推荐结果
        optimized_data = self._optimize_combination_recommendations(optimized_data)
        
        # 2. 优化产品归类结果
        optimized_data = self._optimize_classification_results(optimized_data)
        
        # 3. 确保数据完整性和一致性
        optimized_data = self._ensure_data_consistency(optimized_data)
        
        logger.info("数据优化完成")
        return optimized_data
        
    def _optimize_combination_recommendations(self, data):
        """优化组合包装推荐结果
        
        Args:
            data: 包含组合包装推荐的数据
            
        Returns:
            优化后的组合包装推荐数据
        """
        if data is None or data.empty:
            return data
        
        # 确保组合ID的唯一性和一致性
        if "组合ID" in data.columns:
            # 统计每个组合ID的记录数
            combination_counts = data["组合ID"].value_counts()
            
            # 移除只有一条记录的组合ID（可能是错误标记）
            invalid_combination_ids = combination_counts[combination_counts == 1].index
            data.loc[data["组合ID"].isin(invalid_combination_ids), "组合ID"] = pd.NA
        
        return data
        
    def _optimize_classification_results(self, data):
        """优化产品归类结果
        
        Args:
            data: 包含产品归类结果的数据
            
        Returns:
            优化后的产品归类数据
        """
        if data is None or data.empty:
            return data
        
        # 确保产品类别的一致性
        if "产品类别" in data.columns and "备注" in data.columns:
            from src.config.constants import CLASSIFICATION_CATEGORIES
            
            # 为不同备注类型设置相应的默认产品类别
            category_mapping = {
                CLASSIFICATION_CATEGORIES["REUSE"]: "标准产品",
                CLASSIFICATION_CATEGORIES["REFERENCE_INDEPENDENT"]: "参考产品-独立包装",
                CLASSIFICATION_CATEGORIES["REFERENCE_COMBINATION"]: "参考产品-组合包装",
                CLASSIFICATION_CATEGORIES["REFERENCE_MIXED"]: "参考产品-混合包装",
                "设计项": "设计产品"
            }
            
            # 填充缺失的产品类别
            for note, category in category_mapping.items():
                mask = (data["备注"] == note) & (data["产品类别"].isna() | (data["产品类别"] == ""))
                data.loc[mask, "产品类别"] = category
        
        return data
        
    def _ensure_data_consistency(self, data):
        """确保数据的完整性和一致性
        
        Args:
            data: 需要检查一致性的数据
            
        Returns:
            数据一致性检查通过的数据
        """
        if data is None or data.empty:
            return data
        
        # 确保关键字段的一致性
        if "数量" in data.columns and "预估数量" in data.columns:
            # 当数量已知时，使用实际数量
            mask = data["数量"].notna() & data["预估数量"].notna()
            data.loc[mask, "预估数量"] = data.loc[mask, "数量"]
        
        # 确保日期格式一致性
        if "预完工日期" in data.columns:
            # 转换为统一的日期格式
            try:
                data["预完工日期"] = pd.to_datetime(data["预完工日期"])
                data["预完工日期"] = data["预完工日期"].dt.strftime("%Y-%m-%d")
            except Exception as e:
                import logging
                logger = logging.getLogger("result_processing")
                logger.warning(f"日期格式标准化失败: {str(e)}")
        
        return data

    def export_results(self, optimized_data, output_file_path):
        """导出处理结果到Excel文件

        Args:
            optimized_data: 优化后的结果数据
            output_file_path: 输出文件路径

        Raises:
            ExportError: 导出失败
        """
        if optimized_data is None or optimized_data.empty:
            raise ExportError("没有可导出的数据")

        try:
            # 确保输出目录存在
            output_dir = os.path.dirname(output_file_path)
            if output_dir and not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)

            # 导出数据到Excel文件
            self.excel_utils.export_to_excel(
                optimized_data,
                output_file_path,
                sheet_name=EXPORT_SETTINGS.get(
                    "DEFAULT_SHEET_NAME", "包装方案查询结果"
                ),
            )

            # 验证导出的数据
            self._validate_exported_data(optimized_data, output_file_path)

        except Exception as e:
            raise ExportError(f"导出数据失败: {str(e)}")

    def _validate_exported_data(self, original_data, exported_file_path):
        """验证导出的数据是否一致

        Args:
            original_data: 原始数据
            exported_file_path: 导出文件路径

        Raises:
            DataConsistencyError: 数据不一致
        """
        # 读取导出的文件
        exported_data = self.excel_utils.read_excel(exported_file_path)

        # 检查记录数量是否一致
        if len(exported_data) != len(original_data):
            raise DataConsistencyError(
                f"导出的数据记录数量不一致: 原始记录数={len(original_data)}, "
                f"导出记录数={len(exported_data)}"
            )

        # 检查关键字段是否一致
        # 核心关键字段列表
        key_columns = ["客户简称", "规格型号", "数量", "备注", "包装类型"]

        # 检查关键字段是否存在
        for col in key_columns:
            if col not in exported_data.columns:
                raise DataConsistencyError(f"导出文件中缺少关键字段: {col}")
            if col not in original_data.columns:
                continue  # 原始数据可能没有此字段，跳过检查

            # 比较字段值
            # 处理可能的类型转换问题
            original_col = original_data[col].fillna("")
            exported_col = exported_data[col].fillna("")

            # 转换数值类型以便比较
            if pd.api.types.is_numeric_dtype(original_col):
                original_col = original_col.astype(float)
                exported_col = exported_col.astype(float)
            else:
                original_col = original_col.astype(str)
                exported_col = exported_col.astype(str)

            # 找出不一致的行
            diff_mask = original_col != exported_col
            if diff_mask.any():
                diff_indices = diff_mask[diff_mask].index.tolist()
                sample_diff = min(
                    5, len(diff_indices)
                )  # 最多显示5个不一致的示例
                raise DataConsistencyError(
                    f"导出数据与原始数据在字段'{col}'上不一致。不一致的行索引示例: {diff_indices[:sample_diff]}..."
                )

        # 检查产品类别描述是否正确
        if (
            "产品类别描述" in original_data.columns
            and "产品类别描述" in exported_data.columns
        ):
            category_diff_mask = original_data["产品类别描述"].fillna(
                ""
            ) != exported_data["产品类别描述"].fillna("")
            if category_diff_mask.any():
                diff_indices = category_diff_mask[
                    category_diff_mask
                ].index.tolist()
                raise DataConsistencyError(
                    f"导出数据与原始数据在'产品类别描述'上不一致。不一致的行索引: {diff_indices[:5]}..."
                )

        # 检查组合ID一致性（如果存在）
        if (
            "组合ID" in original_data.columns
            and "组合ID" in exported_data.columns
        ):
            # 由于Excel可能会将NA转换为0或空字符串，所以我们需要特殊处理
            original_combination_ids = original_data["组合ID"].dropna()
            exported_combination_ids = exported_data["组合ID"].dropna()

            if len(original_combination_ids) != len(exported_combination_ids):
                raise DataConsistencyError(
                    f"导出数据与原始数据的组合ID数量不一致: 原始数量={len(original_combination_ids)}, "
                    f"导出数量={len(exported_combination_ids)}"
                )
